iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0
Modern Web

擁抱 .Net Core系列 第 20

[Day20] 中介軟體 Middleware - 2

  • 分享至 

  • xImage
  •  

說到Middleware,一定要提一下核心物件HttpContext物件

回顧一下昨天提到的圖

在圖中可以看到Middleware是一棒傳一棒往下傳遞HttpRequestHttpResponse
但實際上所傳遞的並不是HttpRequestHttpResponse
而是HttpContxt物件,裏面包含了HttpRequestHttpResponse還有一些其他的資訊

在介紹HttpContext的屬性前,我們先介紹一個比較簡單的Middleware使用方式吧

Middleware

實際上Middleware就是一個RequestDelegate的委派
RequestDelegate.cs

public delegate Task RequestDelegate(HttpContext context);

正如前面所提的參數為HttpContext

來看個比較簡單的用法吧

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 自訂Middleware,當HttpMethod 為Get的時候固定回傳"It a GET request"
// 其他方法則繼續往下走,並竄改回應
app.Use(async (context, next) =>
{
    if (context.Request.Method == "GET")
    {
        await context.Response.WriteAsync("It a GET request");
        return;
    }

    await next();
    await context.Response.WriteAsync("回應已被竄改");
});

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

await app.RunAsync();

在這邊使用dotnet 6 minimal api 作為範例
插入了一個middleware

app.Use(async (context, next) =>
{
    if (context.Request.Method == "GET")
    {
        await context.Response.WriteAsync("It a GET request");
        return;
    }

    await next();
    await context.Response.WriteAsync("回應已被竄改");
});

context 是HttpContext 物件
next 就是上面提到的RequestDelegate,當呼叫next()或使用next.Invoke()的時候
會將管線交給下一棒
因此,通常會在next() 前處理請求, next()處理回應

app.Use(async (context, next) =>
{
    // handle request
    await next();
    // handle response
});

在這個middleware中,http方法為 Get的請求會被我攔截不往後傳遞
也就是不會進到Controller中
回應則會被竄改

https://ithelp.ithome.com.tw/upload/images/20221001/20109549KbGPPFYdRy.png

其他Middleware的建立方式我打算留到明天XD

HttpContext

HttpContxt.cs

public abstract class HttpContext
{
    public abstract IFeatureCollection Features { get; }
    public abstract HttpRequest Request { get; }
    public abstract HttpResponse Response { get; }
    public abstract ConnectionInfo Connection { get; }
    public abstract WebSocketManager WebSockets { get; }
    public abstract ClaimsPrincipal User { get; set; }
    public abstract IDictionary<object, object?> Items { get; set; }
    public abstract IServiceProvider RequestServices { get; set; }
    public abstract CancellationToken RequestAborted { get; set; }
    public abstract string TraceIdentifier { get; set; }
    public abstract ISession Session { get; set; }
    public abstract void Abort();
}

因為大部分屬性都如字面上的意思,所以並不打算每個都介紹

  • IFeatureCollection: 放Serve轉出來的Feature
  • HttpRequest: 毫無反應就是Http請求
  • HttpResponse: 同上,Http回應
  • ConnectionInfo: Http連線資訊
  • WebSocketManager: 管理與控制WebSocket的咚咚
  • ClaimsPrincipal: 關於Request User 的令牌(驗證身分的資訊)
  • Items: 可以存放一些要在後面的middleware 所需的資訊
  • RequestServices: DI老朋友IServiceProvider,用來提供當前Request所需的服務
  • RequestAborted: 當Client 中斷或Timeout 時,可以透過該CancelToken 進行後續操作(RollbackDB等等)
  • TraceIdentifier: 可以自訂及取得追蹤碼
  • ISession: 中文翻會話,就是Server端的紀錄
  • Abort: 中斷Http請求,DefaultHttpContext的Abort 甚麼事都不會做source code

items

當有些資訊你在前面的Middleware中取得了且後面不想花功夫再拿的時候可以使用
舉例,在middle的其中一步使用到了secret
且後續另個middleware也需要secret時,可以丟進items

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<FakeRepository>();
var count = 0;

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.Use(async (context, next) =>
{
    var requestCount = Interlocked.Increment(ref count);
    requestCount %= 3;
    var fakeRepository = context.RequestServices.GetRequiredService<FakeRepository>();
    var requestSecret = fakeRepository.Get(requestCount);
    context.Items.Add(nameof(requestSecret), requestSecret);
    await next();
});

app.Use(async (context, next) =>
{
    var requestSecret = context.Items["requestSecret"] as string;
    if (requestSecret != "Two")
    {
        context.Response.StatusCode = 401;
        return;
    }
    await next();
});

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

await app.RunAsync();

public class FakeRepository
{
    private new Dictionary<int, string> _fakeData = new()
    {
        {0,"Zero"},
        { 1, "One" },
        { 2, "Two" },
    };
    
    public string Get(int id)
    {
        return _fakeData[id];
    }
}

上一篇
[Day19] 中介軟體 Middleware - 1
下一篇
[Day20] 中介軟體 Middleware - 3
系列文
擁抱 .Net Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言